package paper.graph;

import java.util.Random;
import paper.db.PaperIndex;

public class Matrix 
{
    public static final double BIG=10000000;
    double[][] cnts;
    int rows, cols;
    public Matrix(int r, int c)
    {
        int[] dims = { r,c };
        cnts = new double[r][c];
        rows = r;
        cols = c;
    }

    public void set(int row, int col, double content)
    {
        cnts[row][col] = content;
    }

    public double get(int row, int col)
    {
        return cnts[row][col];
    }

    public Matrix( Matrix c )
    {
        rows = c.rows;
        cols = c.cols;
        cnts = new double[rows][cols]; 
        for(int i=0; i<rows; i++)
            for( int j=0; j<cols; j++)
                cnts[i][j] = c.cnts[i][j];
    }

    /* Generate a random adjacency matrix where the degree of
       the vertices follows a power-law
    */
    public void randomiseP()
    {
        Random rand = new Random();
        for(int r=0; r<rows; r++)
        {
            double p = 0.5/(double)(r+1);
            for(int c=0; c<cols; c++)
                cnts[r][c] = (rand.nextDouble()<p) ? 1 : 0;
        }
    }

    /* Generate a random adjacency matrix where the degree of
       the vertices is uniformly distributed
    */
    public void randomiseU()
    {
        Random rand = new Random();
        double p = 1/(double)rows;
        for(int r=0; r<rows; r++)
            for(int c=0; c<cols; c++)
                cnts[r][c] = (rand.nextDouble()<p) ? 1 : 0;
    }

    public interface GraphBuilder<E extends Edge,V extends Vertex>
    {
        PaperGraph<E,V> makeGraph();
        E makeEdge(String a, String b);
    }

    public PaperGraph makeGraph( GraphBuilder builder)
    {
        PaperGraph g = builder.makeGraph();
        for(int r=0; r<rows; r++)
            for(int c=r+1; c<cols; c++)
                if( cnts[r][c]==1 )
                    g.addEdge( Integer.toString(r),
                               Integer.toString(c),
                               builder.makeEdge(Integer.toString(r),Integer.toString(c)) );
        return g;
    }

    public void clear(double x)
    {
        for(int i=0; i<rows; i++)
        {
            for(int j=0; j<cols; j++)
                cnts[i][j] = x;
        }
    }

    public void dump()
    {
        for(int i=0; i<rows; i++)
        {
            for(int j=0; j<rows; j++)
                System.out.print( cnts[i][j] + " " );
            System.out.println("");
        }
    }

    /* The fold method makes a Matrix symmetric. Both
       (i,j) and (j,i) are set to the maximum of the
       zero values. This enforces a symmetry across the
       diagonal. The diagonal itself is left unchanged.
       For an adjacency matrix this adds all necessary
       edges to make uni-directional links bi-directional.
       Ie to project the digraph onto a graph.
    */
    public void fold()
    {
        for(int r=0; r<rows; r++)
            for(int c=r+1; c<cols; c++)
                cnts[r][c] = max( cnts[r][c], cnts[c][r] );   
    }

    double max(double a, double b)
    {
        if( a>b )
            return a;
        return b;
    }

    double min(double a, double b)
    {
        if( a<b )
            return a;
        return b;
    }

    public void multiply( Matrix other )
    {
        Matrix copy = new Matrix(this);
        for(int i=0; i<rows; i++)
        {
            for( int j=0; j<cols; j++)
            {
                double x = BIG;
                for( int k=0; k<rows; k++)  
                {
                    double res;
                    if( copy.cnts[i][k] == BIG || other.cnts[k][j] == BIG)
                        res = BIG;
                    else
                        res = copy.cnts[i][k] + other.cnts[k][j];
                    x = min(x,res);
                }
                cnts[i][j] = x;
            }
        }
    }

}

